package nr.co.mhgames.polyanim.renderer.modifiers;

import java.awt.Color;

import nr.co.mhgames.polyanim.util.ColorDef;

/**
 * Color modifier allows you to color the animation by blending into a single
 * color. Three types of coloring are supported: blending, addition and
 * converting to grey scale. Blending just interpolates between the blending
 * color specified and the original polygon color. Addition adds the blending
 * color on top of the original polygon color, building up to white like normal
 * addition blending.
 * <p>
 * Note that if both HSL and Color modifiers are used together, HSL is always
 * applied first, then the coloring. Also, while the transformation modifiers
 * (flip, rotate, etc.) can be chained, only one instance of HSL and Color
 * modifiers can be active. So, if you pass several of these modifiers to the
 * renderer, only the last modifier will be applied.
 * 
 * @author Mika Halttunen
 * @remark This modifier makes the renderer perform the colorization on every
 *         single polygon of the animation it renders, so the quite possible
 *         performance hit caused by that should be taken into consideration.
 */
public class ColorModifier implements RenderModifier {
	/** Coloring types */
	public static enum ColoringType {
		GREYSCALE, BLEND, ADD
	}

	/** Desired coloring type */
	public ColoringType type;

	/** Color to blend with */
	public final ColorDef colorMod = new ColorDef();

	/** Blending factor (zero means the original color, one means the new color) */
	public float factor;


	/**
	 * Constructor.
	 * 
	 * @param color
	 *            Blending color (unused when converting to grey scale, so can
	 *            be <code>null</code>)
	 * @param factor
	 *            Blending factor (0.0f - 1.0f)
	 * @param type
	 *            Coloring type
	 */
	public ColorModifier(ColorDef color, float factor, ColoringType type) {
		if (factor < 0)
			factor = 0;
		else if (factor > 1.0f)
			factor = 1.0f;

		if (color != null)
			this.colorMod.setFrom(color);
		this.factor = factor;
		this.type = type;
	}

	/** @see {@link ColorModifier#ColorModifier(ColorDef, float, ColoringType)} */
	public ColorModifier(Color color, float factor, ColoringType type) {
		this(new ColorDef(color), factor, type);
	}

	/**
	 * Colorize the given original color with the new color specified in the
	 * modifier constructor. Original color alpha is not changed. The changes
	 * are applied to the given color instance.
	 * 
	 * @param color
	 *            Original color that will be colorized
	 */
	public void transformColor(ColorDef color) {
		if (type == ColoringType.BLEND) {
			// Blend with the original color
			int rDiff = colorMod.red - color.red;
			int gDiff = colorMod.green - color.green;
			int bDiff = colorMod.blue - color.blue;

			int nR = color.red + (int) (rDiff * factor);
			int nG = color.green + (int) (gDiff * factor);
			int nB = color.blue + (int) (bDiff * factor);

			color.set(nR, nG, nB, color.alpha);
		} else if (type == ColoringType.ADD) {
			// Add to the original color
			int nR = color.red + (int) (colorMod.red * factor);
			int nG = color.green + (int) (colorMod.green * factor);
			int nB = color.blue + (int) (colorMod.blue * factor);

			// Clamp
			if (nR > 255)
				nR = 255;
			if (nG > 255)
				nG = 255;
			if (nB > 255)
				nB = 255;

			color.set(nR, nG, nB, color.alpha);
		} else {
			// Convert to grey scale
			int g = (int) (color.red * 0.299f + color.green * 0.587f + color.blue * 0.114f);
			assert (g >= 0 && g < 256);

			// Blend with the original color
			int rDiff = g - color.red;
			int gDiff = g - color.green;
			int bDiff = g - color.blue;

			int nR = color.red + (int) (rDiff * factor);
			int nG = color.green + (int) (gDiff * factor);
			int nB = color.blue + (int) (bDiff * factor);

			color.set(nR, nG, nB, color.alpha);
		}
	}
}
